package loader

import (
	"os"
	"time"
	"io/fs"
	"errors"
	"io/ioutil"
	"path/filepath"
)


type FileSystem interface {
	Open(path string) (File, error)
}
type File interface {
	Close() error
	Info() (os.FileInfo, error)
	ReadDir() ([] os.FileInfo, error)
	ReadContent() ([] byte, error)
}
type FileRef struct {
	FS    FileSystem
	Path  string
}
func ReadFile(path string, fs FileSystem) ([] byte, error) {
	var f, err = fs.Open(path)
	if err != nil { return nil, err }
	defer (func() {
		_ = f.Close()
	})()
	return f.ReadContent()
}
func ReadFileOrFolder(path string, fs FileSystem) (os.FileInfo, [] byte, [] os.FileInfo, error) {
	var f, err1 = fs.Open(path)
	if err1 != nil { return nil, nil, nil, err1 }
	defer (func() {
		_ = f.Close()
	})()
	var info, err2 = f.Info()
	if err2 != nil { return nil, nil, nil, err2 }
	if info.IsDir() {
		var entries, err = f.ReadDir()
		if err != nil { return nil, nil, nil, err }
		return info, nil, entries, nil
	} else {
		var content, err = f.ReadContent()
		if err != nil { return nil, nil, nil, err }
		return info, content, nil, nil
	}
}

type RealFileSystem struct {}
type RealFile struct {
	Fd  *os.File
}
func (_ RealFileSystem) Open(path string) (File, error) {
	var fd, err = os.Open(path)
	if err != nil { return nil, err }
	return RealFile { fd }, nil
}
func (f RealFile) Close() error {
	return f.Fd.Close()
}
func (f RealFile) Info() (os.FileInfo, error) {
	return f.Fd.Stat()
}
func (f RealFile) ReadDir() ([] os.FileInfo, error) {
	return f.Fd.Readdir(0)
}
func (f RealFile) ReadContent() ([] byte, error) {
	return ioutil.ReadAll(f.Fd)
}

type StdFileSystem struct {
	FS  fs.FS
}
type StdFile struct {
	File  fs.File
}
func StdFileSystemNormalizePath(path string) string {
	return filepath.ToSlash(path)
}
func (std StdFileSystem) Open(path string) (File, error) {
	var f, err = std.FS.Open(StdFileSystemNormalizePath(path))
	if err != nil { return nil, err }
	return StdFile { f }, nil
}
func (f StdFile) Close() error {
	return f.File.Close()
}
func (f StdFile) Info() (os.FileInfo, error) {
	return f.File.Stat()
}
func (f StdFile) ReadDir() ([] os.FileInfo, error) {
	var dir, is_dir = f.File.(fs.ReadDirFile)
	if !(is_dir) { return nil, errors.New("not a directory") }
	var entry_list, err = dir.ReadDir(-1)
	var info_list = make([] os.FileInfo, len(entry_list))
	for i, entry := range entry_list {
		var info, err = entry.Info()
		if err != nil { return nil, err }
		info_list[i] = info
	}
	return info_list, err
}
func (f StdFile) ReadContent() ([]byte, error) {
	return ioutil.ReadAll(f.File)
}

type craftedFileInfo struct {
	name     string
	size     int64
	mode     os.FileMode
	modTime  time.Time
	isDir    bool
	sys      interface {}
}
func (info craftedFileInfo) Name() string { return info.name }
func (info craftedFileInfo) Size() int64 { return info.size }
func (info craftedFileInfo) Mode() os.FileMode { return info.mode }
func (info craftedFileInfo) ModTime() time.Time { return info.modTime }
func (info craftedFileInfo) IsDir() bool { return info.isDir }
func (info craftedFileInfo) Sys() interface{} { return info.sys }


